﻿using System;
using System.Collections;
using System.Data;
using System.Linq.Expressions;
using MySql.Simple.Mapper.Structures;

namespace MySql.Simple.Mapper.Linq.Query
{
    public class MySqlQueryContext<T>
    {


        // Executes the expression tree that is passed to it.
        internal object Execute(Expression expression, bool isEnumerable, Connection connection)
        {
            // The expression must represent a query over the data source.
            if (!IsQueryOverDataSource(expression))
                throw new InvalidProgramException("No query over the data source was specified.");

            // Get the place name(s) to query the Web service with.
            var sqlGenerator = new SqlGenerator<T>();

            GeneratedSql = sqlGenerator.GenerateSql(expression);

            var results = new ArrayList();
            
            using (var db = connection.Open())
            {
                using (var command = db.CreateCommand())
                {
                    command.CommandText = GeneratedSql;
                    command.CommandType = CommandType.Text;

                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            object obj;

                            if (isEnumerable)
                            {
                                var type = typeof(T).GetInnerElementType();

                                obj = DataReaderBinder.Bind(reader, type);
                            }
                            else
                            {
                                obj = (T)DataReaderBinder.Bind(reader, typeof(T));
                            }

                            results.Add(obj);
                        }
                    }
                }
            }

            if (isEnumerable)
            {
                var type = typeof(T).GetInnerElementType();

                return results.ToArray(type);
            }

            return results.ToArray(typeof(T));
        }

        private static bool IsQueryOverDataSource(Expression expression)
        {
            // If expression represents an unqueried IQueryable data source instance,
            // expression is of type ConstantExpression, not MethodCallExpression.
            return (expression is MethodCallExpression);
        }

        /// <summary>
        /// Gets or sets the generated SQL.
        /// </summary>
        /// <value>The generated SQL.</value>
        public string GeneratedSql { get; private set; }
    }

    public class InnermostWhereFinder : ExpressionVisitor
    {
        private MethodCallExpression innermostWhereExpression;

        public MethodCallExpression GetInnermostWhere(Expression expression)
        {
            Visit(expression);
            return innermostWhereExpression;
        }

        protected override Expression VisitMethodCall(MethodCallExpression expression)
        {
            if (expression.Method.Name == "Where")
                innermostWhereExpression = expression;

            Visit(expression.Arguments[0]);

            return expression;
        }
    }
}